#include "types.h"
#include "params.h"
#include "biscuit.h"
#include "memory.h"
#include "lock.h"
#include "kalloc.h"
#include "proc.h"
#include "trap.h"
#include "kfunc.h"
#include "syscall.h"
#include "traphdlr.h"

extern char trampoline[]; 
extern char uservec[];
extern char userret[];

void usertrap ();
void kerneltrap ();
int handle_exintrp (uint32 cause); // 外部中断
int handle_syscall (); // 系统调用
int handle_inexcp (uint32 cause); // 内部异常
struct trapframe * tf;
extern void * kpgtb;

void
trapinit () {
    ll_w_mtvec ((void*)TRAMPOLINE + (uservec-trampoline));
}


void
kerneltrap () {
    #define IS_EXTERNAL_INTRP(cause) ((cause & EXTERNAL_INTRP_MASK) != 0)
    #define IS_SYSCALL(cause) (cause == EXCP_SYSCALL_U)
    uint32 cause = ll_r_mcause ();
    panic ("kernel trap");
    if (IS_EXTERNAL_INTRP (cause))
        handle_exintrp (cause);
    else
        handle_inexcp (cause);
}

void
seeyou (void* trapframe, void* pgtb) {
    void(*ret)(void*,void*) = ((void*)TRAMPOLINE + (userret-trampoline));
    ret(trapframe, pgtb);
}

void
usertrap () {
    unsigned int cause;
    unsigned int status;
    struct proc * p;
    p = myproc ();
    p->mepc = ll_r_mepc ();
    cause = ll_r_mcause ();
    //ll_halt ();
    ll_w_mtvec (kerneltrap);
    //intr_on ();
    //printk ("usertrap");
    if (IS_EXTERNAL_INTRP (cause)) {
        panic ("external intrp");
        handle_exintrp (cause);
    } else if (IS_SYSCALL (cause))  {
        //panic ("syscall");
        handle_syscall ();
        p->mepc+=4;
    } else {
        panic ("internal intrp");
        handle_inexcp (cause);
    }
    intr_off ();
    ll_w_mtvec ((void*)TRAMPOLINE + (uservec-trampoline));
    status = ll_r_mstatus ();
    set_mstatus_mpie (status, 1);
    ll_w_mstatus (status);
    // ll_w_mtvec (usertrap);
    ll_w_mepc (p->mepc);
    seeyou (tf, p->pgtb);
}

int
handle_exintrp (uint32 cause) {
    cause &= ~EXTERNAL_INTRP_MASK;
    switch (cause) {
        case INTRP_CHRDEV_GETC : {
            cons_intrhdlr ();
            break;
        }
        case INTRP_MFS_RET : {
            mfs_intrhdlr ();
            break;
        }
        default:
        break;
    }
    return 0;
}

int
handle_syscall () {
    // printk ("handling syscall\n");
    //panic ("syscall0");
    struct proc * p = myproc ();
    tf = p->tf;
    return syscall (tf->a0, tf->a1, tf->a2, tf->a3);
}

int
handle_inexcp (uint32 cause) {
    printk ("cause 0x%x, ptr:%p\n", cause, ll_r_mtval());
    panic ("inexcp");
    switch (cause) {
        case EXCP_INST_MISALIGN : {
            psudo_intrhdlr ("PC EXCP_INST_MISALIGN.\n");
            break;
        }
        case EXCP_ADDR_MISALIGN : {
            psudo_intrhdlr ("EXCP_ADDR_MISALIGN.\n");
            break;
        }
        case EXCP_UNDEF_INST : {
            psudo_intrhdlr ("EXCP_UNDEF_INST.\n");
            break;
        }
        case EXCP_ILLGAL_INST : {
            illinst_hdlr ();
            break;
        }
        case EXCP_INST_PGFT : {
            psudo_intrhdlr ("EXCP_INST_PGFT.\n");
            break;
        }
        case EXCP_LOAD_PGFT : {
            psudo_intrhdlr ("EXCP_LOAD_PGFT.\n");
            break;
        }
        case EXCP_STOR_PGFT : {
            psudo_intrhdlr ("EXCP_STOR_PGFT.\n");
            break;
        }
        case EXCP_ACCESS_MIS_X : {
            missing_x_hdlr ();
            break;
        }
        case EXCP_ACCESS_MIS_R : {
            psudo_intrhdlr ("EXCP_ACCESS_MIS_R.\n");
            break;
        }
        case EXCP_ACCESS_MIS_W : {
            psudo_intrhdlr ("EXCP_ACCESS_MIS_W.\n");
            break;
        }
        case EXCP_ACCESS_MIS_U : {
            psudo_intrhdlr ("EXCP_ACCESS_MIS_U.\n");
            break;
        }
        case EXCP_ACCESS_INVADDR : {
            psudo_intrhdlr ("EXCP_ACCESS_INVADDR.\n");
            break;
        }
        case EXCP_TIMER : {
            timer_intrhdlr ();
            break;
        }
        case EXCP_DIVZERO : {
            psudo_intrhdlr ("EXCP_DIVZERO.\n");
            break;
        }
        case EXCP_VMNIMPL : {
            psudo_intrhdlr ("EXCP_VMNIMPL.\n");
            break;
        }
        default:
        break;
    }
    return 0;
}